package flowdroid;

import cli.ConfigManager;
import soot.G;
import soot.Scene;
import soot.jimple.infoflow.InfoflowConfiguration;
import soot.jimple.infoflow.android.InfoflowAndroidConfiguration;
import soot.jimple.infoflow.android.SetupApplication;
import soot.jimple.infoflow.android.resources.ARSCFileParser;
import soot.jimple.infoflow.methodSummary.data.provider.EagerSummaryProvider;
import soot.jimple.infoflow.methodSummary.taintWrappers.SummaryTaintWrapper;
import soot.options.Options;
import util.ApkSizeAnalyzer;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collections;

/**
 * My Custom Config Helper for Soot/FlowDroid Configuration Management
 */
public class MyFlowConfigManager {
    private final SetupApplication app;
    private final InfoflowAndroidConfiguration infoAndroConfig;

    public static void resetEnvironment() {
        G.reset();
        Scene.v().reset();
        System.gc();
        ARSCFileParser.STRICT_MODE = false;
    }

    public MyFlowConfigManager(SetupApplication setupApplication) {
        this.app = setupApplication;
        this.infoAndroConfig = setupApplication.getConfig();
    }

    public void configureAll(String apkFileLocation, String androidJarPlatform, String classpath, boolean openReconstruction, ConfigManager configManager) throws URISyntaxException, IOException {
        configureSootOptions(apkFileLocation, androidJarPlatform, classpath);
        configureInfoflowConfiguration();
        configurePath(openReconstruction);
        configureAccessPath();
        configureSummary();
        // 线性自适应配置 数据流分析时间、单个路径重建时间、路径重建总时间
        configureTimeControl(apkFileLocation,configManager);
    }

    public void configureSootOptions(String apkFileLocation, String androidJarPlatform, String classpath) {
        Options v = Options.v();
        v.set_soot_classpath(classpath);
        v.set_src_prec(Options.src_prec_apk);
        v.set_output_format(Options.output_format_jimple);
        v.set_output_dir("JimpleOutput");
        v.set_process_multiple_dex(true);
        v.set_keep_line_number(true);
        v.set_prepend_classpath(true);
        v.set_no_bodies_for_excluded(true);
        v.set_allow_phantom_refs(true);
        v.set_process_dir(Collections.singletonList(apkFileLocation));
        v.set_android_jars(androidJarPlatform);
        v.set_whole_program(true);
        v.ignore_resolution_errors();
        app.getSootConfig().setSootOptions(v, infoAndroConfig);
    }

    private void configureInfoflowConfiguration() {
        infoAndroConfig.setSootIntegrationMode(InfoflowConfiguration.SootIntegrationMode.CreateNewInstance);
//        infoAndroConfig.setDataFlowTimeout(1200L);
        infoAndroConfig.setCallgraphAlgorithm(InfoflowConfiguration.CallgraphAlgorithm.SPARK);
        infoAndroConfig.setTaintAnalysisEnabled(true);
        infoAndroConfig.setLogSourcesAndSinks(true);
        infoAndroConfig.setMergeDexFiles(true);
        infoAndroConfig.setWriteOutputFiles(false);
//        infoAndroConfig.setWriteOutputFiles(true); // debug
        infoAndroConfig.setPathAgnosticResults(false);
        infoAndroConfig.setEnableLineNumbers(true);
        infoAndroConfig.setCodeEliminationMode(InfoflowConfiguration.CodeEliminationMode.PropagateConstants);
        infoAndroConfig.setAliasingAlgorithm(InfoflowConfiguration.AliasingAlgorithm.FlowSensitive);
        infoAndroConfig.setMemoryThreshold(0.9);
        infoAndroConfig.setFilterConditionalSinks(false);
        infoAndroConfig.setEnableExceptionTracking(false);
        infoAndroConfig.setIncrementalResultReporting(false);
        infoAndroConfig.setStaticFieldTrackingMode(InfoflowConfiguration.StaticFieldTrackingMode.None);
    }

    private void configurePath(boolean openReconstruction) {
        InfoflowConfiguration.PathConfiguration pathConfiguration = infoAndroConfig.getPathConfiguration();
//        pathConfiguration.setPathReconstructionTimeout(60);
        if (openReconstruction) {
            pathConfiguration.setPathReconstructionMode(InfoflowConfiguration.PathReconstructionMode.Fast);
//            pathConfiguration.setPathReconstructionTimeout(60); //60 seconds
            pathConfiguration.setPathReconstructionBatchSize(3);
        } else {
            pathConfiguration.setPathReconstructionMode(InfoflowConfiguration.PathReconstructionMode.NoPaths);
        }
    }

    // 线性自适应配置 数据流分析时间、单个路径重建时间、路径重建总时间
    private void configureTimeControl(String apkFileLocation, ConfigManager configManager) {
        ApkSizeAnalyzer analyzer = new ApkSizeAnalyzer();
//        analyzer.defaultAnalyze(apkFileLocation);
        analyzer.setAnalyzeResource(apkFileLocation,configManager);
        infoAndroConfig.setDataFlowTimeout(analyzer.dataflowTime);
        InfoflowConfiguration.PathConfiguration pathConfiguration = infoAndroConfig.getPathConfiguration();
        pathConfiguration.setPathReconstructionTimeout(analyzer.pathReconstructionTime);

        pathConfiguration.setPathReconstructionTotalTime(analyzer.pathReconstructionTotal);
    }

    private void configureAccessPath() {
        InfoflowConfiguration.AccessPathConfiguration accessPathConfiguration = infoAndroConfig.getAccessPathConfiguration();
        accessPathConfiguration.setAccessPathLength(75);
        infoAndroConfig.getSolverConfiguration().setMaxAbstractionPathLength(-1);
    }

    private void configureSummary() throws URISyntaxException, IOException {
        EagerSummaryProvider summariesManual = new EagerSummaryProvider("summariesManual");
        SummaryTaintWrapper summaryTaintWrapper = new SummaryTaintWrapper(summariesManual);
        app.setTaintWrapper(summaryTaintWrapper);
    }
}